// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.WindowsCE.Forms;
using svs.Mobile.BeyondMedia.Controls;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.RSS;
using svs.Mobile.BeyondMedia.Forms;
using svs.Mobile.BeyondMedia.Transitions;
using svs.Mobile.BeyondMedia.UiCore;

namespace svs.Mobile.BeyondMedia.Objects
{
	internal class ViewManager
	{
		#region TitleType enum

		public enum TitleType
		{
			Default,
			Custom
		}

		#endregion

		private static TitleType m_CurrentTitleType = TitleType.Default;

		private static Helpers.ViewNames m_CurrentViewName = Helpers.ViewNames.Feeds;
		private static Helpers.ViewNames m_LastTrackView = Helpers.ViewNames.FeedContent;
		private static readonly List<Control> m_views = new List<Control>(6);
		
		/// <summary>
		/// Initializes the specified i_ feeds.
		/// </summary>
		public static void Initialize(Control i_Feeds, Control i_Tracks, Control i_Player, Control i_FeedContent, Control i_WebBrowser, Control i_Categories)
		{
			m_views.Clear();

			m_views.Add(i_Categories); 
			m_views.Add(i_Feeds);
			m_views.Add(i_Tracks);
			m_views.Add(i_Player);
			m_views.Add(i_FeedContent);
			m_views.Add(i_WebBrowser);

			//Subscribe to various events
			EnclosureDownloadManager.EnclosureDownloadFailed += OnEnclosureDownloadFailed;
			EnclosureDownloadManager.EnclosureDownloadTerminated += OnEnclosureDownloadTerminated;
			EnclosureDownloadManager.EnclosureDownloadProgress += OnEnclosureDownloadProgress;
			EnclosureDownloadManager.EnclosureDownloadStarted += OnEnclosureDownloadStarted;
			EnclosureDownloadManager.EnclosureDownloadCompleted += OnEnclosureDownloadCompleted;
			EnclosureDownloadManager.DownloadBatchCanceled += OnDownloadBatchCanceled;
			EnclosureDownloadManager.DownloadBatchCompleted += OnDownloadBatchCompleted;
			EnclosureDownloadManager.EnclosureDownloadStarting += OnEnclosureDownloadStarting;

			UpdateAndDownloadManager.ProcessingStarted += OnProcessingStarted;
			UpdateAndDownloadManager.ProcessingCanceled += OnProcessingCanceled;
			UpdateAndDownloadManager.ProcessingCompleted += OnProcessingCompleted;

			RssFeedUpdateManager.UpdateCanceled += OnUpdateCanceled;
			RssFeedUpdateManager.UpdateStarting += OnUpdateStarting;
			RssFeedUpdateManager.UpdateCompleted += OnUpdateCompleted;
			RssFeedUpdateManager.FeedChanged += OnFeedChanged;
			RssFeedUpdateManager.FeedUpdateProgress += OnFeedUpdateProgress;

			FeedRepository.TrackDeleted += OnTrackDeleted;
			//FeedRepository.RepositorySave += OnRepositorySave;
			FeedRepository.TrackChanged += OnTrackChanged;
			FeedRepository.FeedChanged += OnFeedChanged;

			MediaPlayer.MediaPlayerPlayStateChanged += OnPlayerStateChanged;

			MobileDevice.Hibernate += OnMobileDeviceHibenrate;
		}

		private static void OnMobileDeviceHibenrate(object sender, EventArgs e)
		{
			
		}

		private static void OnPlayerStateChanged(Track track)
		{
			if(Configuration.AutoScreenOffOnPlayback && track.CurrentPlayState == Track.PlayMode.Play && PlatformDetection.IsPocketPC())
			{
				DelayedCommandExecutor.ExecuteCommand(10000, delegate
				                                            	{
				                                            		if (CurrentViewName == Helpers.ViewNames.Player && MediaPlayer.IsPlaying)
				                                            			CommandManager.CmdTurnBacklightOff();
				                                            	});
			}
		}

		private static void OnTrackChanged(Track aTrack)
		{
			RefreshTracksView(aTrack);
		}

		/*private static void OnRepositorySave(bool isSaving)
		{
			try
			{
				//Add a "'" to indicate the repository is saving
				if (Master.MasterForm.InvokeRequired)
				{
					// use 'BeginInvoke' instead of 'Invoke'. The event will be scheduled via PostMessage() and the
          // call will not wait until the main tread is idle to process the message.
					Master.MasterForm.BeginInvoke(new CoreHelper.RepositorySaveEvent(OnRepositorySave), isSaving);
					return;
				}

				if (isSaving)
					Master.MasterForm.Text = "'" + Master.MasterForm.Text;
				else
				{
					if (Master.MasterForm.Text.StartsWith("'"))
						Master.MasterForm.Text = Master.MasterForm.Text.Substring(1);
				}
			}
			catch (ObjectDisposedException)
			{
				//In some cases the event can be fired after the main for has been already closed
			}
		}*/

		#region Public Properties

		/// <summary>
		/// Gets or sets the current view.
		/// </summary>
		/// <value>The current view.</value>
		public static Helpers.ViewNames CurrentViewName
		{
			get { return m_CurrentViewName; }
			set { ActivateView(value); }
		}

		/// <summary>
		/// THe Current View
		/// </summary>
		public static IView CurrentView
		{
			get { return m_views[(int)CurrentViewName] as IView; }
		}

		/// <summary>
		/// Gets the feeds.
		/// </summary>
		/// <value>The feeds.</value>
		public static IFeedsView Feeds
		{
			get { return m_views[(int)Helpers.ViewNames.Feeds] as IFeedsView; }
		}

		/// <summary>
		/// Gets the tracks.
		/// </summary>
		/// <value>The tracks.</value>
		public static ITracksView Tracks
		{
			get { return m_views[(int)Helpers.ViewNames.Tracks] as ITracksView; }
		}

		/// <summary>
		/// Gets the media player.
		/// </summary>
		/// <value>The media player.</value>
		public static MediaPlayerView MediaPlayer
		{
			get { return m_views[(int)Helpers.ViewNames.Player] as MediaPlayerView; }
		}

		/// <summary>
		/// Gets the Feed Content view.
		/// </summary>
		/// <value>The content view.</value>
		public static RSSFeedContentView FeedContent
		{
			get { return m_views[(int)Helpers.ViewNames.FeedContent] as RSSFeedContentView; }
		}

		/// <summary>
		/// Gets the web browser view.
		/// </summary>
		/// <value>The web browser.</value>
		public static WebBrowserView WebBrowser
		{
			get
			{
				return m_views[(int)Helpers.ViewNames.WebBrowser] as WebBrowserView;
			}
		}

		/// <summary>
		/// Categories View
		/// </summary>
		public static KineticCategoryListView Categories
		{
			get
			{
				return m_views[(int)Helpers.ViewNames.Categories] as KineticCategoryListView;
			}
		}

		/// <summary>
		/// Remebers the last view for feed items (either track or browser)
		/// </summary>
		public static Helpers.ViewNames LastFeedItemsView
		{
			get { return m_LastTrackView; }
		}

		#endregion

		#region Public Methods

		#region Delegates

		private delegate void ViewDelegate(Track track);

		public delegate void ActivateViewDelegate(Helpers.ViewNames i_View);

		private delegate void TrySetCustomTitleDelegate(string title);

		private delegate void SetCustomTitleDelegate(string title, bool lockTitle);

		#endregion

		private static void ActivateView(Helpers.ViewNames i_View)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new ActivateViewDelegate(ActivateView), new object[] {i_View});
				return;
			}

			var from = m_views[(int)m_CurrentViewName] as IView;
			var to = m_views[(int)i_View] as IView;

			ViewTransitionManager.Execute(from, to, CalculateTransitionType(from,to));
		}

		private static ViewTransitionManager.TransitionType CalculateTransitionType(IView from, IView to)
		{
			if(from == to)
				return ViewTransitionManager.TransitionType.Basic;

			int fromIndex = m_views.IndexOf(from as Control);
			int toIndex = m_views.IndexOf(to as Control);

			if (from == Tracks && to == MediaPlayer)
				return ViewTransitionManager.TransitionType.FlipRight;

			if (from == MediaPlayer && to == Tracks)
				return ViewTransitionManager.TransitionType.FlipLeft;

			if(fromIndex < 4 && toIndex < 4)
			{
				return fromIndex < toIndex ? ViewTransitionManager.TransitionType.SlideLeft : ViewTransitionManager.TransitionType.SlideRight;
			}
			
			return ViewTransitionManager.TransitionType.Basic;
		}

		public static void RefreshTitle()
		{
			TrySetTitle(CurrentView.Title);
		}

		public static void TrySetTitle(string title)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new TrySetCustomTitleDelegate(TrySetTitle), title);
				return;
			}

			if(m_CurrentTitleType != TitleType.Custom)
				Master.MasterForm.Text = title;
		}

		public static void SetCustomTitle(string title, bool lockTitle)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new SetCustomTitleDelegate(SetCustomTitle), title, lockTitle);
				return;
			}

			m_CurrentTitleType = lockTitle ? TitleType.Custom : TitleType.Default;

			try
			{
				Master.MasterForm.Text = title;
			}
			catch
			{
				//Do Nothing there
			}
		}

		#endregion

		#region Event Handlers

		static void OnFeedChanged(Feed feed)
		{
			RefreshViews(feed);
		}

		private static void OnTrackDeleted(Track t)
		{
			RefreshContentView(t);
		}

		private static void OnUpdateCompleted()
		{
			if (Configuration.FeedsSortOrder == CoreHelper.SortOrder.ByDateAsc ||
			    Configuration.FeedsSortOrder == CoreHelper.SortOrder.ByDateDesc)
			{
				Feeds.Reload();
			}

			FeedRepository.SaveRepository();

			VersionCheckManager.CheckForNewVersionIfNeeded();

			if(!UpdateAndDownloadManager.IsWorking)
				SetCustomTitle(string.Format("Update Completed!"), false);
		}

		private static void OnUpdateStarting()
		{
			SetCustomTitle(string.Format("Updating... ({0} left)", RssFeedUpdateManager.QueueCount), true);
		}

		private static void OnUpdateCanceled()
		{
			SetCustomTitle(string.Format("Update Canceled!"), false);
		}

		private static void OnProcessingCompleted(int numItems)
		{
			if (numItems > 0)
				SetCustomTitle(string.Format("Nothing to Download!"), false);
			else
				SetCustomTitle(string.Format("Update Completed!"), false);
		}

		private static void OnProcessingCanceled(int numItems)
		{
			SetCustomTitle(string.Format("Canceled!"), false);
		}

		private static void OnProcessingStarted(int numItems)
		{
			SetCustomTitle(string.Format("Processing..."), true);
		}

		private static void OnEnclosureDownloadStarting(Track t, bool isRetrying)
		{
			if (t == null || !isRetrying)
				SetCustomTitle(string.Format("Downloading... ({0} left)", EnclosureDownloadManager.QueueCount), true);
			else
				SetCustomTitle(string.Format("Retrying... ({0} left)", EnclosureDownloadManager.QueueCount), true);
		}

		private static void OnDownloadBatchCompleted()
		{
			SetCustomTitle(
				string.Format("Downloaded {0} of {1}!",
				              EnclosureDownloadManager.DownloadCount - EnclosureDownloadManager.FailedDownloadsCount,
				              EnclosureDownloadManager.DownloadCount), false);
		}

		private static void OnDownloadBatchCanceled()
		{
			SetCustomTitle(string.Format("Download canceled!"), false);
			ReloadFeedsView();
		}

		private static void OnEnclosureDownloadCompleted(Track t)
		{
			RefreshContentView(t);
			RefreshTracksView(t);
			ReloadFeedsView();
		}

		private static void OnEnclosureDownloadStarted(Track t)
		{
			ReloadFeedsView();
			RefreshTracksView(t);
		}

		private static void OnEnclosureDownloadTerminated(Track t)
		{
			RefreshTracksView(t);
			ReloadFeedsView();
		}

		private static void OnEnclosureDownloadProgress(Track t)
		{
			if(t.DownloadStatus != Track.DownloadStatuses.Downloading) // somewtimes we can get a single progress messge even after the download was canceled
				return;

			var info = t.DownloadSize <= 0 ? string.Format("{1} - {0}",t.DownloadStatus,CoreHelper.GetFileLengthAsString(t.DownloadedPortion)) :
																					string.Format("{1}% of {2} - {0}...",t.DownloadStatus,(int) t.DownloadPercent,CoreHelper.GetFileLengthAsString(t.DownloadSize));

			SetCustomTitle(string.Format("{0} - {1}",EnclosureDownloadManager.QueueCount,info), true);

			RefreshTracksView(t);
		}

		private static void OnEnclosureDownloadFailed(Track t)
		{
			ReloadFeedsView();
		}

		private static void OnFeedUpdateProgress(Feed feed)
		{
			if (feed.UpdateSize > 2048 || feed.UpdatedPortion > 2048)
			{
				var info = feed.UpdateSize <= 0 ? string.Format("{0} - Updating...", CoreHelper.GetFileLengthAsString(feed.UpdatedPortion)):
				           														string.Format("{0}% of {1}", feed.UpdatePercent, CoreHelper.GetFileLengthAsString(feed.UpdateSize));

				SetCustomTitle(string.Format("{0} - {1}", RssFeedUpdateManager.QueueCount, info), true);
			}
				
			RefreshFeedsView(feed);
		}

		public static void OnViewTransitionCompleted(IView fromView, IView toView)
		{
			m_CurrentViewName = (Helpers.ViewNames)m_views.IndexOf(toView as Control);

			Master.MasterForm.Menu = CurrentView.ViewMenu;

			if (m_CurrentViewName == Helpers.ViewNames.Tracks || m_CurrentViewName == Helpers.ViewNames.FeedContent)
			{
				m_LastTrackView = m_CurrentViewName;
			}

			Master.MasterForm.MinimizeBox = m_CurrentViewName == Helpers.ViewNames.Feeds;

			if (fromView != null)
				fromView.OnAfterDeactivate();

			toView.OnAfterActivate();

			RefreshTitle();
		}

		#endregion

		#region PrivateMethods

		private static void RefreshTracksView(Track track)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new ViewDelegate(RefreshTracksView), track);
				return;
			}

			//If we are displaying the view of the current track
			if (Tracks.IsShowingFeed(track.ParentFeed))
			{
				Tracks.NotifyTrackStatusChanged(track);
			}
		}

		private static void RefreshFeedsView(Feed feed)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new RefreshViewsDelegate(RefreshFeedsView),feed);
				return;
			}

			Feeds.NotifyFeedStatusChanged(feed);
		}

		private static void ReloadFeedsView()
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new RefreshViewDelegate(ReloadFeedsView));
				return;
			}

			Feeds.Reload();
		}

		private static void RefreshContentView(Track track)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new ViewDelegate(RefreshContentView), track);
				return;
			}

			FeedContent.UpdateDownloadStaus(track.ParentFeed, track);
		}

		private static void RefreshViews(Feed aFeed)
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new RefreshViewsDelegate(RefreshViews), aFeed);
				return;
			}

			if (aFeed != null)
			{
				if (aFeed.HasNewContent)
					FeedContent.UpdateDownloadStaus(aFeed, null);

				if (RssFeedUpdateManager.IsAtQueueStart)
					Feeds.SelectedFeed = aFeed;
			}

			RefreshFeedsView(aFeed);
		}

		private delegate void RefreshViewsDelegate(Feed aFeed);

		#endregion

		#region Nested type: RefreshViewDelegate

		private delegate void RefreshViewDelegate();

		#endregion

		#region Nested type: ViewDelegate

		#endregion
	}
}